﻿//------------------------------------------------------------------------------------------------- 
// <copyright file="ContextPluggableAttribute.cs" company="Christopher Hines">
// Copyright (c) Christopher Hines. All rights reserved.
// </copyright>
// <summary>
//    Marks a decorated class as able to be plugged for a ContextPlugin type
// </summary>
//-------------------------------------------------------------------------------------------------

namespace Copperfield
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// Marks a decorated class as able to be plugged for a ContextPlugin type
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
    public sealed class ContextPluggableAttribute : Attribute
    {
        /// <summary>
        /// Gets the related contexts delegate
        /// </summary>
        private readonly Func<Type, IEnumerable<IComparable>> relatedContextsDelegate;

        /// <summary>
        /// Gets the plugin type
        /// </summary>
        private readonly Type pluginType;

        /// <summary>
        /// Gets the context type
        /// </summary>
        private readonly Type contextType;

        /// <summary>
        /// Initializes a new instance of the ContextPluggableAttribute class
        /// </summary>
        /// <param name="contextType">The type of context to which the attribute applies</param>
        /// <param name="pluginType">The type of plugin for which the decorated class can be plugged</param>
        public ContextPluggableAttribute(Type contextType, Type pluginType)
        {
            this.relatedContextsDelegate = TypeUtility.GetRelatedContextsDelegate(contextType);

            this.pluginType = pluginType;

            this.contextType = contextType;
        }

        /// <summary>
        /// Gets the plugin type
        /// </summary>
        public Type PluginType
        {
            get { return this.pluginType; }
        }

        /// <summary>
        /// Gets the context type
        /// </summary>
        public Type ContextType
        {
            get { return this.contextType; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the decorated class should be treated as the default instance of the plugin type
        /// </summary>
        public bool DefaultInstance { get; set; }

        /// <summary>
        /// Builds a list of contexts that are compatible with a specified type
        /// </summary>
        /// <param name="type">The type for which compatible contexts should be generated</param>
        /// <returns>The list of compatible contexts</returns>
        public IEnumerable<IComparable> BuildRelatedContextsForType(Type type)
        {
            return this.relatedContextsDelegate != null
                       ? this.relatedContextsDelegate(type) ?? new List<IComparable>()
                       : new List<IComparable>();
        }
    }
}
